home *** CD-ROM | disk | FTP | other *** search
/ Libris Britannia 4 / science library(b).zip / science library(b) / PROGRAMM / PASCAL / 0921.ZIP / MODIFY.ARC / MODIF.PAS
Pascal/Delphi Source File  |  1988-01-22  |  10KB  |  225 lines

  1. PROGRAM modify4;
  2.   {  Version B, 12/23/87.  Minor bug fixes, additional comments added
  3.   regarding how to deal with headers which fall across the boundaries
  4.   of your search buffer.}
  5.  
  6.   {  This is an update of Modify 1.00, which is designed to
  7.   allowing quick cloning under Turbo version 3.  This now
  8.   works under version 4.  Address questions and bug reports
  9.   to Bob Tolz, Compuserve ID #70475,1071.  Original comments for
  10.   the modify version 1.00 follow.  Please read them with a grain
  11.   of salt, given the changes required for upgrading to 4.0}
  12.  
  13.   {----------  Begin Modify 1.00 Comments ------------}
  14.   {  PROGRAM Modify_Turbo_COM_File; }
  15.   {  For DOS versions of Turbo only }
  16.  
  17.   (*
  18.   Purpose of Modify:
  19.   Like the popular CLONE.PAS and CLOVER.PAS, Modify provides a method
  20.   by which Turbo programmers can allow users to create customized versions
  21.   of their programs without recompiling the source.
  22.  
  23.   How it works:
  24.   Modify takes advantage of the fact that Turbo stores typed constants
  25.   in the code segment.  This means that the location of a given typed
  26.   constant in a .COM file will always correspond to its offset from CSeg, less
  27.   the size of the Program Segment Prefix ($100 bytes).  An integer variable
  28.   (typed constant) located at CSeg:$0500 while a program is in memory, for
  29.   example, can be found by Seek-ing to the 1024th byte (1024 = $400) in the
  30.   .COM file.  If you were then to write the current value of the integer
  31.   variable to disk, the current value would become the default or initial
  32.   value.  This is what Modify does, essentially, except that it can change
  33.   multiple variables in one swoop.
  34.  
  35.   How to use the Modify function:
  36.   The demonstration program included here shows how to use Modify, but
  37.   I'll explain the procedure briefly anyway:
  38.   First, declare all the variables that you want to be able to modify
  39.   as typed constants (global) in a single cluster.  The cluster should
  40.   begin with a "header string", probably the name of the program and perhaps
  41.   a version number as well.  This will help Modify insure that the .COM
  42.   file being modified is the right one.  (Be sure that the header string is
  43.   unique--if you release an updated version of your Modify-able program,
  44.   you should change the header string.)  The end of the cluster should be
  45.   marked by a one-byte dummy variable, such as "Tail" in the demo program.
  46.   In between you can put as many variables as you wish.
  47.   Modifying your program is then simple: just call the Modify function
  48.   with three parameters: the name of the .COM file, the header string, and
  49.   the dummy variable used to mark the end of the cluster.  Modify takes
  50.   care of everything else, and returns an error code to indicate what
  51.   happened.  (See the list of return codes, below.)
  52.  
  53.   Modify lacks some of the features of CLONE and CLOVER--it can't make
  54.   backup versions of programs, for example--and it is slightly riskier
  55.   (in my own programs, I ask the user to confirm that he has a backup
  56.   copy of the program before calling Modify).  But it is the smallest,
  57.   fastest, and simplest routine to clone COM files that I've seen, and
  58.   I think that many people will find that its advantages more than
  59.   compensate for its disadvantages.  (I almost forgot: Modify, like
  60.   Clover, works fine for programs with overlays.  It will not, however,
  61.   work with EXE files created by Turbo Extender.  Randy Forgaard is
  62.   reportedly working on a simlar routine that will.)
  63.  
  64.   Special thanks to Randy Forgaard for his suggestions, as well as to Bob
  65.   Tolz, Bela Lubkin, and James Troutman.  Please address all comments,
  66.   suggestions, etc. to:
  67.   Brian Foley, CompuServe ID #76317,3247.
  68.   *)
  69.   {----------  End Modify 1.00 Comments ------------}
  70.  
  71.  
  72.   Uses
  73.   tpstring;                   (*
  74.                               From Turbo Professional 4.0, produced by TurboPower Software,
  75.                               to use their Search function.  This is not required if you can
  76.                               create your own routine to search through a buffer for a string
  77.                               match.  I am not employed by Turbo Power Software; I just use
  78.                               all their products.  You can reach them at 408-438-8608, or
  79.                               through Compuserve ID# 72457,2131.
  80.                               -RDT *)
  81.  
  82. TYPE
  83.   _FileName = STRING[64];
  84.  
  85. CONST
  86.  
  87.   {--Cluster of Modify-able variables starts here--}
  88.   Headlength = 11;
  89.   Head : STRING[11] = 'Modify 4.00';
  90.   ProgramName : STRING[64] = 'MODIFY.EXE';
  91.   {Used to keep track of name changes.}
  92.   Changes : Integer = 0;      { Number of times demo has been MODIFY-ed. }
  93.   Example : STRING[80] = 'Welcome to MODIFY.EXE.';
  94.   Tail : Byte = 0;
  95.   {--Cluster ends here--}
  96.  
  97.  
  98.   FUNCTION Modify(FName : _FileName; VAR Head, Tail) : Byte;
  99.     {
  100.     Return codes:
  101.     0 : FName was modified successfully.
  102.     1 : Unable to open FName.
  103.     2 : Error reading FName.
  104.     3 : Error writing to FName.
  105.     5 : Wrong .COM file.
  106.     }
  107.   CONST filebufferlength = 1024;
  108.     {See WARNING!!! below}
  109.  
  110.   VAR
  111.     filebuffer : ARRAY[1..filebufferlength] OF Byte;
  112.     F : FILE;
  113.     Data : ARRAY[1..256] OF Byte ABSOLUTE Head;
  114.     Check : STRING[255] ABSOLUTE Head;
  115.     Actual : STRING[255];
  116.     checksize,
  117.     DataSize, modresult,
  118.     Result : Integer;
  119.     fileoffset, sizeoffile, seekpos : longint;
  120.     searchresult : word;
  121.     foundmatch : Boolean;
  122.  
  123.   BEGIN
  124.     Modresult := 0;           { Assume success }
  125.     Assign(F, FName);
  126.     {$I-}
  127.     Reset(F, 1);
  128.     {$I+}
  129.     IF IOResult <> 0 THEN BEGIN
  130.       Modresult := 1;         { File wasn't found or couldn't be opened }
  131.     END
  132.     ELSE BEGIN                {level 1}
  133.       sizeoffile := FileSize(f);
  134.       foundmatch := False;
  135.       fileoffset := 0;
  136.       searchresult := 0;
  137.       seekpos := sizeoffile;
  138.       WHILE NOT foundmatch AND NOT((seekpos-filebufferlength) < 0) DO
  139.         BEGIN
  140.           seekpos := seekpos-filebufferlength;
  141.           Seek(f, seekpos);
  142.           BlockRead(f, filebuffer, filebufferlength);
  143.           searchresult := search(filebuffer, filebufferlength, head, headlength);
  144.  
  145.           (*  Search is a function from Turbo Professional 4.0's unit TPSTRING.
  146.           If you don't have that package (you should), then you'll need to
  147.           write your own routine to search the buffer for the headerstring.  *)
  148.  
  149.           IF NOT(searchresult = $FFFF) THEN
  150.             BEGIN
  151.               foundmatch := True;
  152.               fileoffset := seekpos+searchresult;
  153.             END;
  154.         END;
  155.  
  156.       {WARNING!!!  If your header string does not fall ENTIRELY WITHIN
  157.       the buffer which was searched, but instead spans two buffer reads,
  158.       then the search for your header will fail with this algorithm.  The
  159.       chances are probably slim given the large size of FILEBUFFERLENGTH,
  160.       but if you can't find your header, then look here as the first cause.
  161.       You could make this code more bulletproof by forcing the routine to
  162.       search around the boundaries of each buffer if the search fails, but
  163.       probably the simplest thing to do is change FILEBUFFERLENGTH (either
  164.       up or down) by the size of your header string to make sure the header
  165.       falls entirely within a buffer}
  166.  
  167.       {$I-}
  168.       IF foundmatch THEN
  169.         Seek(F, fileoffset);
  170.       {$I+}
  171.       IF IOResult <> 0 THEN
  172.         Modresult := 2        { Error when trying to locate header }
  173.       ELSE BEGIN              {level 2}
  174.         CheckSize := Succ(Ord(Check[0])); { Length of the header string + 1 }
  175.         BlockRead(F, Actual, CheckSize, Result);
  176.         IF (Result <> CheckSize) (*OR (Actual <> Check)*) THEN BEGIN
  177.           IF Result <> CheckSize THEN Modresult := 2 { Read error }
  178.             (*{ Actual <> Check } ELSE Modresult := 5; { Header strings don't match }*)
  179.             (*This is left over from version 1.0 for Turbo 3.0, it's
  180.             unnecessary here since we've specifically searched for the
  181.             header string above*)
  182.         END
  183.         ELSE BEGIN            {level 3}
  184.           DataSize := Ofs(Tail)-(Ofs(Head)+CheckSize);
  185.           { Now, write everything--from the byte just beyond the header string
  186.           to the tail marker--out to disk.  File pointer already points to
  187.           the first byte after the header string. }
  188.           BlockWrite(F, Data[Succ(CheckSize)], DataSize, Result);
  189.           IF (Result <> DataSize) THEN Modresult := 3; { Disk write error }
  190.         END;                  {level 3}
  191.       END;                    {level 2}
  192.     END;                      {level 1}
  193.     Close(F);
  194.     modify := modresult;
  195.   END;
  196.  
  197.  
  198.  
  199. BEGIN                         { Demonstration }
  200.   IF ProgramName <> 'MODIFY.EXE' THEN
  201.     WriteLn('You''ve changed the program''s name to "', ProgramName, '"');
  202.   WriteLn(ProgramName, ' has been changed ', Changes, ' time(s).');
  203.   Changes := Succ(Changes);
  204.   WriteLn('The current example string is "', Example, '"');
  205.   Write('Enter a new one: ');
  206.   ReadLn(Example);
  207.   IF Modify(ProgramName, Head, Tail) <> 0 THEN BEGIN
  208.     { Assume the problem is simply that the file wasn't found.
  209.     This assumption is not a very good one, but OK for the
  210.     purposes of the demo program. }
  211.     WriteLn('Enter the full file name, or [Return] to exit.');
  212.     Write('Full name: ');
  213.     ReadLn(ProgramName);
  214.     IF ProgramName = '' THEN WriteLn('No changes made.')
  215.     ELSE CASE Modify(ProgramName, Head, Tail) OF
  216.       0 : WriteLn(ProgramName, ' has been modified successfully.');
  217.       1 : WriteLn('Unable to open ', ProgramName);
  218.       2 : WriteLn('Error reading from ', ProgramName);
  219.       3 : WriteLn('Error writing to ', ProgramName);
  220.       5 : WriteLn('Wrong .EXE file: ', ProgramName);
  221.     END;
  222.   END
  223.   ELSE WriteLn(ProgramName, ' has been modified successfully.');
  224. END.
  225.